package Image.segment;

import Image.SegmentedImage;
import java.awt.image.BufferedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.media.jai.JAI;
import javax.media.jai.ROI;
import javax.media.jai.RenderedOp;

/**
 * @author Seth Wessitsh <Seth@wessitsh.com>
 */
public class Segmenter {
	public static File segwinFile = new File("C:/temp/extract/jseg/segwin.exe");
	public static Integer colorQuantThresh = 600;
	public static Double segmentMergeThresh = 0.7;

	/**
	 * Allows the path of the segwin executable to be set at runtime.
	 *
	 * @param path the path were segwin should be located
	 */
	public static void setSegWinPath(File path) {
		if (path.isDirectory()) {
			segwinFile = new File(path.getPath() + File.separator + "segwin.exe");
		}
	}

	/**
	 * Loads the segments for <code>imageToSegment</code>.  If the .map.gif file 
	 * corresponding to the image to be segmented is within the same directory (described
	 * in {@link SegmentedImage}) and can be loaded then it will be.  Otherwise the 
	 * method delegates to {@link #createSegmentMap(Image.SegmentedImage)} to have it
	 * created. Once the segment map is available the ROI objects are created and added to
	 * <code>imageToSegment</code> via the
	 * {@link SegmentedImage#setSegments(java.util.Vector)} method.
	 *
	 * @param imageToSegment
	 */
	public static void createSegments(SegmentedImage imageToSegment) {
		/**
		 * Check to see if we have the segment map gif, create it if not
		 */
		if (imageToSegment.getSegmentMap() == null) {
			createSegmentMap(imageToSegment);
		}

		BufferedImage image = imageToSegment.getSegmentMap();
		Vector<Segment> segments = new Vector<Segment>();

		/**
		 * Find the number of segments by finding the largest index in the gif
		 */
		ParameterBlock pb = new ParameterBlock();
		pb.addSource(image); 
		pb.add(null); pb.add(4); pb.add(4);

		RenderedOp ro = JAI.create("extrema", pb);
		int numSegs = (int) ((double[]) ro.getProperty("maximum"))[0];

		/**
		 * Create ROIs and Segments
		 * 
		 * ROI(image,i) creates a region which includes all pixels with value greater
		 * than or equal to i so we must subtract subRegions from superRegions to get an
		 * ROI which only includes the threshold i.
		 *
		 * For some reason the superRegion.subtract(subRegion) doesn't work for the 0th
		 * index so we skip that, which means that we are actually missing one segment
		 * of the image.
		 * @todo see if there is a way to get the 0th segment
		 */
		int i = 1;
		ROI superRegion = new ROI(image,i),
		    subRegion = new ROI(image,i+1);

		for (i=2; i <= numSegs; i++) {
			segments.add(new Segment(
				superRegion.subtract(subRegion), imageToSegment, i-2
			));
			superRegion = subRegion;
			subRegion = new ROI(image, i+1);
		}
		// add the last segment
		segments.add(new Segment(
			superRegion, imageToSegment, i-2
		));

		/**
		 * Add the segments to the Image object
		 */
		imageToSegment.setSegments(segments);
	}

	/**
	 * Calls segwin to segment the image imageToMap
	 *
	 * It is expected that segwin in the same directory as the images which were loaded
	 * for training
	 * 
	 * @param imageToMap the image to create the map for
	 */
	private static void createSegmentMap(SegmentedImage imageToMap) {
		File segmentMapFile = imageToMap.getSegmentMapFile();
		File originalFile = imageToMap.getImageFile();
		String directory = 
			originalFile.getParentFile().getAbsolutePath() + File.separator;
		String fileName = originalFile.getName();
		String fileNamePrefix = fileName.substring(0, fileName.indexOf("."));

		// run segwin to create the image map
		Vector<String> command = new Vector<String>();
		/* 
		 * Command is similar to the following:
		 * segwin -i file.jpg -t 6 -o file.out.jpg 0.5 -r9 file.map.gif -q 600 -m 0.7
		 * see segwin for description of the arguments
		 */
		command.add(segwinFile.getAbsolutePath());
		command.add("-i ");
		command.add(originalFile.getAbsolutePath());
		command.add("-t ");
		command.add("6");
		command.add("-o ");
		command.add(directory + fileNamePrefix + ".out.jpg");
		command.add("0.5");
		command.add("-r9 ");
		command.add(segmentMapFile.getAbsolutePath());
		command.add("-q ");
		command.add(colorQuantThresh.toString());
		command.add("-m ");
		command.add(segmentMergeThresh.toString());

		System.out.println("Running process: ");
		for (String s : command) {
			System.out.print(s + " ");
		}
		System.out.println();
		
		ProcessBuilder procBuilder = new ProcessBuilder();
		procBuilder.command(command);

		try {
			Process p = procBuilder.start();

			if (p.waitFor() == 1) {
				imageToMap.setSegmentMap(
					ImageIO.read(segmentMapFile)
				);
				System.out.println("Segment map created.");
			} else {
				System.out.println(
					"Unable to create segment map. Return code:" + p.exitValue()
				);
				BufferedReader bis = new BufferedReader(
					new InputStreamReader(p.getErrorStream())
				);
				String line = "";
				while ((line = bis.readLine()) != null) {
					System.out.println(line);
				}

				bis = new BufferedReader(
					new InputStreamReader(p.getInputStream())
				);
				while((line = bis.readLine()) != null) {
					System.out.println(line);
				}
			}
		} catch (IOException ioe) {
			Logger.getLogger(Segmenter.class.getName()).log(Level.SEVERE, null, ioe);
		} catch (InterruptedException ie) {
			Logger.getLogger(Segmenter.class.getName()).log(Level.SEVERE, null, ie);
		}
	}
}